﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;

using DataLite;
using DataLite.Values;

using NVelocity;
using NVelocity.App;
using NVelocity.Context;

namespace DataLite
{
    public class TemplateBuilder
    {
        const string RESOURCES = "Resources";
        const string DATA = "Data";
        const string VALUE = "Value";
        const string MAPPING = "Mapping";
        const string TEST = "Test";       

        const string FILES_EXTENSION = "{0}.cs";

        //Xml con las correspondencias entre tipos bdd al lenguaje
        const string FILE_MAPPING = "mappings.xml";

        //Archivos de plantillas
        const string NV_VALUE_OBJECT = "ValueObject.nv";
        const string NV_IDAO = "IDAO.nv";
        const string NV_MAPPING_OBJECT = "Mapping.nv";
        const string NV_ABSTARCT_DAO = "AbstractDAO.nv";
        const string NV_DAO_IMPLEMENTATION = "DAOImplementation.nv";
        const string NV_TEST_BASIC = "TestBasic.nv";
        const string NV_TEST_DAO = "TestDAO.nv";
        const string NV_FACTORY_DAO = "FactoryDAO.nv";
        const string NV_DAO_VIEW = "DAOView.nv";
        Hashtable mMapping;

        string mResources;

        string mResourcesVO;
        string mResourcesIDAO;
        string mResourcesMapping;
        string mResourcesAbstractDAO;
        string mResourcesDAOImp;
        string mResourcesTestBasic;
        string mResourcesTestDAO;
        string mResourcesFactoryDAO;
        string mResourcesDAOView;

        VelocityEngine engine;

        public TemplateBuilder()
        {
            //Ruta donde se encuentra la dll del generador
            string oRoot = FileHelper.GetAssemblyFolder();

            //plantillas 
            mResources = Path.Combine(oRoot, RESOURCES);

            mResourcesVO = Path.Combine(RESOURCES, NV_VALUE_OBJECT);
            mResourcesIDAO = Path.Combine(RESOURCES, NV_IDAO);
            mResourcesMapping = Path.Combine(RESOURCES, NV_MAPPING_OBJECT);
            mResourcesAbstractDAO = Path.Combine(RESOURCES, NV_ABSTARCT_DAO);
            mResourcesDAOImp = Path.Combine(RESOURCES, NV_DAO_IMPLEMENTATION);
            mResourcesTestBasic = Path.Combine(RESOURCES, NV_TEST_BASIC);
            mResourcesTestDAO = Path.Combine(RESOURCES, NV_TEST_DAO);
            mResourcesFactoryDAO = Path.Combine(RESOURCES, NV_FACTORY_DAO);
            mResourcesDAOView = Path.Combine(RESOURCES, NV_DAO_VIEW);

            //Inicializa hash table con valor db valor lenguaje
            string oXmlDoc = Path.Combine(mResources, FILE_MAPPING);
            mMapping = Tools.LoadConfig(oXmlDoc, "map");

            // Initialize NVelocity
            engine = new VelocityEngine();
            engine.Init();
        }

        string mOutPutRoot;
        public void Build(LiteSchema rLiteSchema, string aNamespace, string aOutput)
        {
            mOutPutRoot = Path.Combine(aOutput, aNamespace);            
            string oOutPutData = Path.Combine(mOutPutRoot, DATA);
            string oOutPutValue = Path.Combine(mOutPutRoot, VALUE);
            string oOutPutMapping = Path.Combine(mOutPutRoot, MAPPING);
            string oOutPutTest = Path.Combine(mOutPutRoot, TEST);

            //Crapetas de la solucion
            FileHelper.CreateSubDirectory(mOutPutRoot);
            FileHelper.CreateSubDirectory(oOutPutMapping, true);
            FileHelper.CreateSubDirectory(oOutPutData, true);
            FileHelper.CreateSubDirectory(oOutPutValue, true);
            FileHelper.CreateSubDirectory(oOutPutTest, true);
            
            VelocityContext context = new VelocityContext();
            context.Put("namespace", aNamespace);

            SaveFile(mResourcesIDAO, "IDAO.cs", context);
            SaveFile(mResourcesAbstractDAO, Path.Combine(DATA, "AbstractDAO.cs"), context);

            List<SqliteMasterVO> relational = new List<SqliteMasterVO>();
            relational.AddRange(rLiteSchema.UserTables);
            relational.AddRange(rLiteSchema.Views);
            foreach (SqliteMasterVO table in relational)
            {  
                table.EntityName = Tools.FormatDbName(table.Name);
                int pkCount = 0;
                foreach (ColumnInfoVO colum in table.ColumnInfo)
                {
                    //Tipo del lenguaje                    
                    colum.Type2 = mMapping[colum.Type].ToString();                    
                    colum.AttName = Tools.FormatDbName(colum.Name);
                    if (colum.Pk == 1) { pkCount++; }
                    if (colum.NotNull != 1) 
                    { 
                        table.AnyIsNull = true;                        
                    }
                }
                table.PkCount = pkCount;

                //tablas sin pk, pk = row_id
                if (pkCount == 0) 
                {
                    ColumnInfoVO oColRowId = new ColumnInfoVO();
                    oColRowId.Name = "rowid";
                    oColRowId.Type = "INTEGER";
                    oColRowId.Type2 = mMapping[oColRowId.Type].ToString();
                    oColRowId.NotNull = 1;
                    oColRowId.Pk = 1;
                    oColRowId.AttName = Tools.FormatDbName(oColRowId.Name);
                    List<ColumnInfoVO> oColumns = new List<ColumnInfoVO>();
                    oColumns.Add(oColRowId);
                    oColumns.AddRange(table.ColumnInfo);
                    table.ColumnInfo = oColumns;
                }                
                
                //Definicion del contexto
                context = new VelocityContext();
                context.Put("namespace", aNamespace);
                context.Put("table", table);
                context.Put("colum", new ColumnInfoVO());

                context.Put("tableName", table.Name);
                context.Put("allFields", Tools.FormatColumns(table));
                context.Put("allParameters", Tools.FormatParameters(table));
                context.Put("columnParameter", Tools.ConditionUpdateAll(table));
                context.Put("condition", Tools.ConditionPrimaryKey(table));
                
                SaveFile(mResourcesVO, Path.Combine(VALUE, table.EntityName + "VO.cs"), context );
                if (table.Type == "view") 
                {
                    SaveFile(mResourcesDAOView, Path.Combine(DATA, table.EntityName + "DAO.cs"), context);
                }
                else
                { 
                    SaveFile(mResourcesDAOImp, Path.Combine(DATA, table.EntityName + "DAO.cs"), context); 
                }
                
                SaveFile(mResourcesMapping, Path.Combine(MAPPING, table.EntityName +".cs" ), context);

                //Test
                SaveFile(mResourcesTestDAO, Path.Combine(TEST, table.EntityName + "Test.cs"), context);                
            }

            context = new VelocityContext();
            context.Put("namespace", aNamespace);
            context.Put("tables", rLiteSchema.UserTables);
            context.Put("table", new SqliteMasterVO());
            SaveFile(mResourcesTestBasic, Path.Combine(TEST, "BasicTest.cs"), context);

            //Factoria
            context.Put("connectionString", rLiteSchema.ConnectionString);
            SaveFile(mResourcesFactoryDAO, "FactoryDAO.cs", context);
        }

        public void SaveFile(string aFileTemplate, string aFileExtension, VelocityContext context)
        {
            Template template = engine.GetTemplate(aFileTemplate);
            string oOutFile = Path.Combine(mOutPutRoot, aFileExtension);

            if (File.Exists(oOutFile)) { File.Delete(oOutFile); }

            StreamWriter streamWriter = new StreamWriter(oOutFile);
            try
            {
                StringWriter stringWriter = new StringWriter();
                template.Merge(context, stringWriter);
                streamWriter.WriteLine(stringWriter.GetStringBuilder().ToString());
            }
            finally
            {
                streamWriter.Close();
            }
        }
    }
}
